package systeme;

import java.util.ArrayList;
import java.util.Arrays;

import rdp.RdP;

/**
 * Classe qui va contenir tous les traitements et tests que l'on peut faire sur
 * un Rdp 
 * En effet la classe RdP ne définit que le modèle
 */
public class AlgoGrapheRdp {

	RdP mon_rdp; // le modele

	public AlgoGrapheRdp(RdP un_rdp) {
		super();
		mon_rdp = un_rdp;
	}

	/**
	 * Parcours toutes les place en entrée de la transition et on regarde si
	 * elles sont toutes marquées
	 * 
	 * @param marquage
	 *            : le marquage du réseau de pétri
	 * @param transition
	 * @return
	 */
	public boolean estFranchissable(boolean[] marquage, int transition) {
		for (int i = 0; i < mon_rdp.pre[transition].length; i++) {
			if (marquage[mon_rdp.pre[transition][i]] == false) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Retourne toutes les transitions franchissables
	 * 
	 * @param
	 * @return
	 */
	public int[] transitionsFranchissables(boolean[] marquageCourant) {
		ArrayList<Integer> res = new ArrayList<Integer>();
		for (int i = 0; i < mon_rdp.transition.length; ++i) {
			if (estFranchissable(marquageCourant, i))
				res.add(i);
		}
		// Recopier l'arraylist dans un tableau
		int[] r = new int[res.size()];
		for (int i = 0; i < res.size(); ++i) {
			r[i] = res.get(i);
		}
		return r;
	}

	/**
	 * Franchir une transition c'est : consommer les jetons en entrée (retirer
	 * un jeton dans chaque pre de cette transition) produire des jetons en
	 * sortie (ajouter un jeton dans chque post de cette transition)
	 * 
	 * @param marquageCourant
	 *            structure actuelle
	 * @param transition
	 * @return le marquage futur
	 */
	public boolean[] franchir(boolean[] marquageCourant, int transition) {
		boolean[] res = new boolean[mon_rdp.place.length];
		// faire une copie de m dans res
		res = Arrays.copyOf(marquageCourant, mon_rdp.place.length);
		// je franchis la transition t a partir de m
		// je parcours les pre conditions et je les mets a faux
		for (int i = 0; i < mon_rdp.pre[transition].length; i++) {
			if (marquageCourant[mon_rdp.pre[transition][i]] == true) {
				res[mon_rdp.pre[transition][i]] = false;
			}
		}
		for (int i = 0; i < mon_rdp.pre[transition].length; i++) {
			if (marquageCourant[mon_rdp.pre[transition][i]] == true) {
				res[mon_rdp.post[transition][i]] = false;
			}
		}
		// je parcours les posts conditions et les mets a vrai
		return res;
	}

	/**
	 * Un Rdp est sain si pour chaque transition t la transition t est saine
	 * 
	 * @return vrai si le rdp est Sain
	 */
	public boolean rdpSain(boolean[] marquageCourant) {
		int[] lesTransitionsFranchissables = transitionsFranchissables(marquageCourant);
		for (int i = 0; i < lesTransitionsFranchissables.length; i++) {
			if (!transitionSaine(marquageCourant, i)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * On part du principe que la transition t
	 * dont on parle est franchissable (testé auparavant par la méthode appelante)
	 * Une transition est saine si elle est franchissable et aucune de ses posts conditions n'est vraie - sauf si la post condition vraie est aussi une pre condition
	 * Par exemple, si on considere la place P1(°) --> Transition T1 ---> place P2() alors c'est correct, car P1 contient un jeton et P2 n'en contient pas (cas général)
	 * autre exemple  : P1(°) ---> Transition T1 ---> P1(°) c'est correct aussi.
	 * Pour simplifier une transition non saine serait une transition qui provoque la présence de deux jetons dans une place. 
	 * @return
	 */
	public boolean transitionSaine(boolean[] marquage, int t) {
		// parcourir la liste des places en post, et vérifier que chacune est
		// bien vide
		for (int i = 0; i < mon_rdp.post[t].length; i++) {
			if (marquage[mon_rdp.post[t][i]] == true) {
				// Je sais que la place en sortie de transition contient un
				// jeton
				// Je dois tester si c'est une place qui est aussi en pre
				// condition
				// Car consommer un jeton et le remmetre au meme endroit est
				// correct
				boolean trouve = false;
				for (int j = 0; j < mon_rdp.pre[t].length; j++) {
					// J'ai trouvé la meme place en pre
					if (mon_rdp.pre[t][j] == mon_rdp.post[t][i]) {
						trouve = true;
					}
				}
				// Si je n'ai pas trouvé,
				// alors la place en pre et la place en post sont deux places différentes
				// Alors la transition n'est pas saine
				if (!trouve) {
					return false;
				}
			}//sinon, la post condition est false, c'est le cas général et c'est correct
		}
		return true;
	}
	
	/**
	 * Fonction qui permet l'instanciation d'un objet GrapheRdp
	 * Cette fonction se contente de faire l'initialisation
	 * et de faire le premier appel a genererGrapheRdp(GrapheRdP g,int etat) qui est une méthode recursive
	 */
	//TODO tester cette fonction sur un cas concret
	public void maingenererGrapheRdp(){
		GrapheRdP g=new GrapheRdP();
		//Executer les différentes transitions 
		g.etat.add(mon_rdp.marquage);
		//Ajouter tous les fils : toutes les transition
		//que l'on peut franchir a partir de l'etat initial
		//Pour chacune des transition franchissables
		int [] lestransitionsFranchissables=transitionsFranchissables(mon_rdp.marquage);
		ArrayList<Integer> mesSucc=new ArrayList<Integer>();
		for(int i=0; i< lestransitionsFranchissables.length; i++){
			boolean [] marquageApresFranchissement=franchir(mon_rdp.marquage,lestransitionsFranchissables[i]);
			//Si cet etat n'a pas deja été atteint 
			//(On est aussi obligé de faire cette verification pour l'etat 0 car une transition peut avoir pour effet de ne pas modifier le marquage apres avoir franchis la transition.
			//Par exemple si on consomme un jeton sur une place et que l'on le remets dans la meme place, alors le marquage n'as pas changé : on est toujours sur l'etat 0
			if(!g.possedeEtatAvecMarquage(marquageApresFranchissement)){
				g.etat.add(marquageApresFranchissement);
				mesSucc.add(i); //etat + i, dans ce cas 0 + i
				genererGrapheRdp(g);
			}
		}
		g.succ.add(mesSucc);		

	}

	/**
	 * Fonction recursive permettant de remplir le contenu de GrahpeRdp.
	 * @param g : le GrapheRdp qui va etre mis a jour avec les etats atteints 
	 */
	//TODO tester cette fonction sur un cas concret
	public void genererGrapheRdp(GrapheRdP g) {
		boolean [] marquage=g.etat.get(g.etat.size()-1); 
		//Ajouter tous les fils : toutes les transition
		//que l'on peut franchir a partir de l'etat initial
		int [] lestransitionsFranchissables=transitionsFranchissables(marquage);
		ArrayList<Integer> mesSucc=new ArrayList<Integer>();
		//Pour chacune des transition franchissables
		for(int t=0; t< lestransitionsFranchissables.length; t++){
			boolean [] marquageApresFranchissement=franchir(marquage,lestransitionsFranchissables[t]);
			//Si cet etat n'a pas deja été atteint
			//C'est a dire si ce marquage n'as pas deja été obtenu par d'autres franchissements
			if(!g.possedeEtatAvecMarquage(marquageApresFranchissement)){
				g.etat.add(marquageApresFranchissement); //j'ajoute le marquage pour cet etat
				mesSucc.add(g.etat.size()-1); //le successeur de l'etat courant est l'indice du dernier ajouté
				genererGrapheRdp(g); //Appel recursif
			}

		}
		//Je renseigne la liste des successeurs de l'etat courant 
		//(on se base sur le fait que l'indice i de l'arraylist contient les valeurs qui correspondent a l'etat i,c'est moche mais pas grave.
		g.succ.add(mesSucc);		

	}
}
